/******************************************************************************\
 * Filename   : enc_ai.h
 * Copyright  : 
 * Created    : 2018-10-31 by litao
 * Description:
 * 
\******************************************************************************/
#include "enc_common.h"
#include "enc_ai.h"

/******************************************************************************
* function : Start Ai
******************************************************************************/
HI_S32 enc_ai_start(AIO_ATTR_S* pstAioAttr, HI_VOID* pstAiVqeAttr, HI_U32 u32AiVqeType)
{
    HI_S32 i;
    HI_S32 s32Ret;
	AUDIO_DEV AiDevId  = ENC_AI_DEV_NUM;
	HI_S32 s32AiChnCnt = pstAioAttr->u32ChnCnt >> pstAioAttr->enSoundmode;
	AUDIO_SAMPLE_RATE_E enOutSampleRate = AUDIO_SAMPLE_RATE_48000;
	HI_BOOL bResampleEn = HI_FALSE;

    if (pstAioAttr->u32ClkChnCnt == 0)
    {
        pstAioAttr->u32ClkChnCnt = 4;
    }

    s32Ret = HI_MPI_AI_SetPubAttr(AiDevId, pstAioAttr);
    if (s32Ret)
    {
        printf("%s: HI_MPI_AI_SetPubAttr(%d) failed with %#x\n", __FUNCTION__, AiDevId, s32Ret);
        return s32Ret;
    }

    s32Ret = HI_MPI_AI_Enable(AiDevId);
    if (s32Ret)
    {
        printf("%s: HI_MPI_AI_Enable(%d) failed with %#x\n", __FUNCTION__, AiDevId, s32Ret);
        return s32Ret;
    }

    for (i = 0; i < s32AiChnCnt; i++)
    {
        s32Ret = HI_MPI_AI_EnableChn(AiDevId, i);
        if (s32Ret)
        {
            printf("%s: HI_MPI_AI_EnableChn(%d,%d) failed with %#x\n", __FUNCTION__, AiDevId, i, s32Ret);
            return s32Ret;
        }

        if (HI_TRUE == bResampleEn)
        {
            s32Ret = HI_MPI_AI_EnableReSmp(AiDevId, i, enOutSampleRate);
            if (s32Ret)
            {
                printf("%s: HI_MPI_AI_EnableReSmp(%d,%d) failed with %#x\n", __FUNCTION__, AiDevId, i, s32Ret);
                return s32Ret;
            }
        }

        if (NULL != pstAiVqeAttr)
        {
			HI_BOOL bAiVqe = HI_TRUE;
			switch (u32AiVqeType)
            {
				case 0:
                    s32Ret = HI_SUCCESS;
					bAiVqe = HI_FALSE;
                    break;
                case 1:
                    s32Ret = HI_MPI_AI_SetVqeAttr(AiDevId, i, 0, i, (AI_VQE_CONFIG_S *)pstAiVqeAttr);
                    break;
                default:
                    s32Ret = HI_FAILURE;
                    break;
            }
            if (s32Ret)
            {
                printf("%s: HI_MPI_AI_SetVqeAttr(%d,%d) failed with %#x\n", __FUNCTION__, AiDevId, i, s32Ret);
                return s32Ret;
            }
			
		    if (bAiVqe)
            {
                s32Ret = HI_MPI_AI_EnableVqe(AiDevId, i);
	            if (s32Ret)
	            {
	                printf("%s: HI_MPI_AI_EnableVqe(%d,%d) failed with %#x\n", __FUNCTION__, AiDevId, i, s32Ret);
	                return s32Ret;
	            }
            }
        }
    }

    return HI_SUCCESS;
}

/******************************************************************************
* function : Stop Ai
******************************************************************************/
HI_S32 enc_ai_stop(AIO_ATTR_S* pstAioAttr, HI_BOOL bVqeEn)
{
    HI_S32 i;
    HI_S32 s32Ret;
	HI_BOOL bResampleEn = HI_FALSE;
	AUDIO_DEV AiDevId  = ENC_AI_DEV_NUM;
	HI_S32 s32AiChnCnt = pstAioAttr->u32ChnCnt >> pstAioAttr->enSoundmode;

    for (i = 0; i < s32AiChnCnt; i++)
    {
        if (HI_TRUE == bResampleEn)
        {
            s32Ret = HI_MPI_AI_DisableReSmp(AiDevId, i);
            if (HI_SUCCESS != s32Ret)
            {
                printf("[Func]:%s [Line]:%d [Info]:%s\n", __FUNCTION__, __LINE__, "failed");
                return s32Ret;
            }
        }

        if (HI_TRUE == bVqeEn)
        {
            s32Ret = HI_MPI_AI_DisableVqe(AiDevId, i);
            if (HI_SUCCESS != s32Ret)
            {
                printf("[Func]:%s [Line]:%d [Info]:%s\n", __FUNCTION__, __LINE__, "failed");
                return s32Ret;
            }
        }

        s32Ret = HI_MPI_AI_DisableChn(AiDevId, i);
        if (HI_SUCCESS != s32Ret)
        {
            printf("[Func]:%s [Line]:%d [Info]:%s\n", __FUNCTION__, __LINE__, "failed");
            return s32Ret;
        }
    }

    s32Ret = HI_MPI_AI_Disable(AiDevId);
    if (HI_SUCCESS != s32Ret)
    {
        printf("[Func]:%s [Line]:%d [Info]:%s\n", __FUNCTION__, __LINE__, "failed");
        return s32Ret;
    }

    return HI_SUCCESS;
}

int enc_send_aframe(HI_S32 audio_chan, AUDIO_FRAME_S *p_audio_frame)
{
	enc_ai_queue *p_ai_queue;
	AUDIO_FRAME_SS *p_frame;

	if (audio_chan > ENC_MAX_CHANNEL) {
		enc_err("audio channel num invalid\n");
		return -1;
	}
	
	p_ai_queue = &main_data.p_ai_block->queue[audio_chan];
	
	sem_wait(p_ai_queue->p_lock);
	p_frame = (AUDIO_FRAME_SS *)p_ai_queue->frame_buffer[p_ai_queue->write_pos];
	p_frame->enBitwidth = p_audio_frame->enBitwidth;
	p_frame->enSoundmode = p_audio_frame->enSoundmode;
	p_frame->u64TimeStamp = p_audio_frame->u64TimeStamp;
	p_frame->u32Seq = p_audio_frame->u32Seq;
	p_frame->u32Len = p_audio_frame->u32Len;
	memcpy(p_frame->data, p_audio_frame->pVirAddr[0], p_audio_frame->u32Len);
	p_ai_queue->write_pos++;
	if (p_ai_queue->write_pos >= ENC_AI_FRAME_MAX_NUM) {
		p_ai_queue->write_pos = 0;
	}
	sem_post(p_ai_queue->p_lock);

	return 0;
}

/******************************************************************************
* function : get frame from Ai, send it  to Aenc or Ao
******************************************************************************/
int enc_ai_get_frame(AIO_ATTR_S *pstAioAttr)
{
	HI_S32 i;
    HI_S32 s32Ret;
    HI_S32 AiFd[ENC_MAX_CHANNEL<<1];
	HI_S32 max_fd = 0;
    AUDIO_FRAME_S stFrame; 
    fd_set read_fds;
	fd_set save_fds;
    struct timeval TimeoutVal;
    AI_CHN_PARAM_S stAiChnPara;
	AUDIO_DEV AiDevId  = ENC_AI_DEV_NUM;
	HI_S32 s32AiChnCnt = pstAioAttr->u32ChnCnt >> pstAioAttr->enSoundmode;	

	if (s32AiChnCnt > (ENC_MAX_CHANNEL<<1)) {
		enc_err("ai channel num invalid\n");
		return HI_FAILURE;
	}

	FD_ZERO(&read_fds);
	for (i=0; i<s32AiChnCnt; i++) {
	    s32Ret = HI_MPI_AI_GetChnParam(AiDevId, i, &stAiChnPara);
	    if (HI_SUCCESS != s32Ret)
	    {
	        printf("%s: Get ai chn param failed\n", __FUNCTION__);
	        return HI_FAILURE;
	    }
	    
	    stAiChnPara.u32UsrFrmDepth = 30;
	    
	    s32Ret = HI_MPI_AI_SetChnParam(AiDevId, i, &stAiChnPara);
	    if (HI_SUCCESS != s32Ret)
	    {
	        printf("%s: set ai chn param failed\n", __FUNCTION__);
	        return HI_FAILURE;
	    }

	    AiFd[i] = HI_MPI_AI_GetFd(AiDevId, i);
	    FD_SET(AiFd[i],&read_fds);	

		if (AiFd[i] > max_fd) 
		{
			max_fd = AiFd[i];
		}
	}
    
	save_fds = read_fds;

    while (g_encset_slv->enc_chlshare.ai_demux_run)
    {     
        TimeoutVal.tv_sec = 1;
        TimeoutVal.tv_usec = 0;

		read_fds = save_fds;
        
        s32Ret = select(max_fd+1, &read_fds, NULL, NULL, &TimeoutVal);
        if (s32Ret < 0) 
        {
        	enc_err("get ai frame select error\n");
            break;
        }
        else if (0 == s32Ret) 
        {
            enc_warn("%s: get ai frame select time out\n", __FUNCTION__);
            continue;
        }        

		for (i=0; i<s32AiChnCnt; i++) {
	        if (FD_ISSET(AiFd[i], &read_fds))
	        {
	            /* get frame from ai chn */
	            s32Ret = HI_MPI_AI_GetFrame(AiDevId, i, &stFrame, NULL, HI_FALSE);
	            if (HI_SUCCESS != s32Ret )
	            {
	                printf("%s: HI_MPI_AI_GetFrame(%d, %d), failed with %#x!\n",\
	                       __FUNCTION__, AiDevId, i, s32Ret);
	                return HI_FAILURE;
	            }

				/* send to app*/
				enc_send_aframe(i%ENC_MAX_CHANNEL, &stFrame);
				
	            /* finally you must release the stream */
	            s32Ret = HI_MPI_AI_ReleaseFrame(AiDevId, i, &stFrame, NULL);
	            if (HI_SUCCESS != s32Ret )
	            {
	                printf("%s: HI_MPI_AI_ReleaseFrame(%d, %d), failed with %#x!\n",\
	                       __FUNCTION__, AiDevId, i, s32Ret);
	                return HI_FAILURE;
	            }
	            
	        }
		}
    }
    
    return HI_SUCCESS;
}

int enc_ai_mem_uninit(void)
{
	int i;
	
	if (main_data.p_ai_block != (void *) (-1) && main_data.p_ai_block != NULL)
	{
		shmdt(main_data.p_ai_block);
	}

	for (i=0; i<ENC_MAX_CHANNEL; i++) {
		if (main_data.p_ai_block->queue[i].p_lock != SEM_FAILED)
		{
			sem_close(main_data.p_ai_block->queue[i].p_lock);
		}	
	}	

	return 0;
}

int enc_ai_mem_init(void)
{
	int i;
	int shrmid;
	char sem_name[16];

	shrmid = shmget((key_t) (ENC_AI_SHRMEM), sizeof(enc_ai_share_block), 0666 | IPC_CREAT);
	if (shrmid < 0)
	{
		enc_err("can't create ai shared_mem!\n");
		return -1;
	}

	main_data.p_ai_block = (enc_ai_share_block *)shmat(shrmid, NULL, 0);
	if (main_data.p_ai_block == (void *) (-1))
	{
		enc_err("can't use ai shared_mem!\n");
		enc_ai_mem_uninit();
		return -1;
	}	

	memset(main_data.p_ai_block, 0, sizeof(enc_ai_share_block));
	for (i=0; i<ENC_MAX_CHANNEL; i++) {
		sprintf(sem_name, "%s%d", ENC_AI_SEMNAME, i);
		main_data.p_ai_block->queue[i].p_lock = sem_open(sem_name, O_CREAT, 0644, 1);
		if (main_data.p_ai_block->queue[i].p_lock == SEM_FAILED)
		{
			enc_err("ai shrmsem_open failed!\n");
			enc_ai_mem_uninit();
			return -1;
		}		
	}

	return 0;
}

int enc_ai_set_attr(AIO_ATTR_S *p_AioAttr)
{
    p_AioAttr->enSamplerate   = AUDIO_SAMPLE_RATE_48000;
    p_AioAttr->enBitwidth     = AUDIO_BIT_WIDTH_16;
    p_AioAttr->enWorkmode     = AIO_MODE_I2S_SLAVE;
    p_AioAttr->enSoundmode    = AUDIO_SOUND_MODE_STEREO;
    p_AioAttr->u32EXFlag      = 0;
    p_AioAttr->u32FrmNum      = 30;
    p_AioAttr->u32PtNumPerFrm = 1024;
    p_AioAttr->u32ChnCnt      = ENC_MAX_CHANNEL;
    p_AioAttr->u32ClkChnCnt   = ENC_MAX_CHANNEL;
    p_AioAttr->u32ClkSel      = 0;		

	return 0;
}

int enc_ai_demux(void)
{
	int ret;
	AIO_ATTR_S stAioAttr;

	ret = enc_ai_mem_init();
	if (ret < 0) {
		return -1;
	}

	enc_ai_set_attr(&stAioAttr);

	ret = enc_ai_start(&stAioAttr, NULL, 0);
	if (ret != 0) {
        enc_err("enc_ai_start error with %d!\n", ret);
        return -1;		
	}	

	g_encset_slv->enc_chlshare.ai_demux_run = 1;
	enc_ai_get_frame(&stAioAttr);

	enc_ai_stop(&stAioAttr, HI_FALSE);

	enc_ai_mem_uninit();

	return 0;
}

